//#include <ilcplex/ilocplex.h>
#include <ilcplex/cplex.h>
#include <stdlib.h>
#include <string.h>
#include "network.h"
#include "cplexhead.h"
#include "master_constraints.h"
#include <iostream>
#include <map>
#include "client.h"
using namespace std;

//Definerar en struct med information till cplex callback
struct cutinfo{
  network * net;
  int numcols;
  cplexhead * head;
  CPXLPptr lp;
};
typedef struct cutinfo CUTINFO, * CUTINFOptr;



double * gety(cplexhead * phead,double * x){

  int y0 =  phead->getmapinfo("y",0);
  int y1 = phead->getmapinfo("y",1); 
  double * y = new double [y1- y0]  ;
  for(int i = y0; i < y1; i++){
      y[i - y0]  = x[i];
      if(y[i-y0] > 0.5){
	y[i-y0] = 1;	
      }else{
	y[i-y0] = 0;
      }     
  }
  return y;

}

static int CPXPUBLIC mycutcallback(CPXCENVptr env, void *cbdata, int wherefrom, void *cbhandle, int * useraction_p){
  cout<<"Running callback!\n";
  int status = 0; 

  //retrevie data
  CUTINFOptr cutinfo = (CUTINFOptr) cbhandle;
  *useraction_p = CPX_CALLBACK_DEFAULT;

  network * net = cutinfo->net;
  int numcols = cutinfo->numcols;
  cplexhead * phead = cutinfo->head; 


 
  //get information about current problem
  double *  x = new double [numcols];
  status = CPXgetcallbacknodex (env, cbdata, wherefrom,x, 0, numcols-1);
  if(status){ cout<<"Failed to get soluton\n";};

 
  int y0 =  phead->getmapinfo("y",0);
  double * y = gety(phead,x);
  
  int NO_SPLIT = 0; //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> CARL fixar?
  int * client_array =  client(net,y,NO_SPLIT);
  if(client_array == NULL){
    return 0;
  }

  int number_of_y_in_cut; int cutnz; double rhs; int number_of_cuts; int addcuts = 0;
  if(NO_SPLIT == 1){
    //reserved spaces
    number_of_cuts = client_array[0];
    number_of_y_in_cut = client_array[1];
   
    cutnz = number_of_y_in_cut;
    rhs = number_of_y_in_cut-1;
    int * cutind = new int [cutnz]; double *  cutval = new double [cutnz]; int idx = 0;
    
    for(int i = 2; i <= number_of_y_in_cut+1; i++){
      cutind[idx] = client_array[i]+y0;
      cutval[idx] = 1;
      idx++;
    }
  
    status = CPXcutcallbackadd(env,cbdata, wherefrom, cutnz, rhs, 'L',cutind,cutval,1);
    cout<<"Satus of adding the client constraint: "<<status<<"\n";
    if(status){
      cout<<"Failed to add cut\n";
    }else{
      addcuts++;
    }

  }else if(NO_SPLIT == 0){
    //ECMP  
    number_of_cuts = client_array[0];
    number_of_y_in_cut = client_array[1];
    cout<<"Number_of_cuts "<<number_of_cuts<<"\n";

    rhs = number_of_y_in_cut -2;
    cutnz = number_of_y_in_cut;
    
    int pos = 0;
    int * cutind = new int [cutnz];
    double *  cutval = new double [cutnz];

    //generate constraints from array
    int idx = 2;
    for(int j = 0; j < number_of_cuts;j++){
      for(int i = 0; i < number_of_y_in_cut; i++) {
	cutind[i] = client_array[idx]+y0;
	idx++;
	
	if(i % number_of_y_in_cut == (number_of_y_in_cut -1)){
	  cutval[i] = -1;
	}else{
	  cutval[i] = 1;	  
	}
      }
      
      for(int i = 0; i < number_of_y_in_cut; i++){
	cout<<"Cutidx: "<<cutval[i]<<" Cutval: "<<cutind[i]<<"\n";
      }
      cout<<"rhs "<<rhs<<"\n";
      status = CPXcutcallbackadd(env,cbdata, wherefrom, cutnz, rhs, 'L',cutind,cutval,1);
      cout<<"satus add: "<<status<<"\n";
      if(status){
	cout<<"Failed to add cut\n";
      }else{
	addcuts++;
      }
      
    }
  }
  
  // after addition of constraints ( for either split or no split )

  CPXLPptr nodelp;
  status = CPXgetcallbacknodelp (env, cbdata, wherefrom, &nodelp);
  if(status){    cout<<"Failed to get lp";}
  // status = CPXwriteprob (env, nodelp, "myck.lp", NULL);
  if(status){ cout<<"Failed to get write prob"; }
 
  if(addcuts > 0){
    *useraction_p = CPX_CALLBACK_SET;
  }
  
  cout<<"Callback done! \n";

  return (status);
  
}




int main (int argc, char **argv){
  int status = 0;
  CPXENVptr env = NULL;
  CPXLPptr lp = NULL;
  int solstat = 0;
  double objval;
  CUTINFO usercutinfo;

  env = makeEnv(status);
  lp = makeLp(env,status);
  //############### Min or Max ##################################
  CPXchgobjsen (env, lp, CPX_MIN);

  network * net = new network();
  net->read_network_file("net.txt");

  //################## Make cplexhead #############################

  int Nodes = net->nodes(); int Edges = net->linktablerows(); int Com = net->nonzerodemands();  
  cplexhead *phead = new cplexhead(Edges,Com,Nodes);
  // map < string, vector<int> > var2 = phead->getmap();
  phead->addvar("x", Edges*Com, 1);
  phead->addvar("y", Edges*Nodes, 1);
  phead->addvar("z", 1, 1);
  phead->addvar("u", Edges, 0);
  phead->addvar("r", Nodes*Nodes, 0);
  phead->addvar("w", Edges, 0);
  phead->addvar("wm", 1, 0);

  //  phead->print();

  usercutinfo.net = net;
  usercutinfo.numcols = phead->getcols();
  usercutinfo.head = phead;
  usercutinfo.lp = lp;
  // ################# Make objective  #############################
   generate_minz_goal(env,lp, &status,phead);

  // ################# make constraints  ############################
   generate_shortestpath_constraint(env,lp, net,&status,phead);
   generate_capacity_constraint_z(env,lp, net,&status,phead);
 
   generate_flow_constraint(env,lp,net,&status, phead);
   generate_no_split_constraint_z( env,lp, net,&status,phead);
   fix_y_from_demand(env,lp, net,&status,phead);

  //####################### set callbacks ##############################
 /*linear mapping between the presoved and original models */
 
  status = CPXsetintparam(env,CPX_PARAM_PRELINEAR, 0);
  if(status) {cout<<"error callback";}
  
  /*turn on traditonal search for use with control callbacks*/
  
  status = CPXsetintparam(env, CPX_PARAM_MIPSEARCH, CPX_MIPSEARCH_TRADITIONAL);
  if(status) {cout<<"error callback";}
  
  /*let mip callbacks work on orignial model*/
  status = CPXsetintparam(env, CPX_PARAM_MIPCBREDLP, CPX_OFF);
  if(status) {cout<<"error callback";}
  
  status = CPXsetintparam(env, CPX_PARAM_REDUCE  , CPX_OFF);
  status = CPXsetcutcallbackfunc(env, mycutcallback, &usercutinfo);
  



  //################## Solve #########################################
  double * x = new double [phead->getcols()];
   status = CPXwriteprob (env, lp, "myprob.lp", NULL);

  status = solveProblemMIP(env,lp,status,&solstat, &objval, &x);
  if(status){cout<<"Error optimizing"; return 0;}
 
 deleteProblem(env ,lp,status);

  return (status);
} /* END main */
